package com.wodan.zhongjinjicang.common.orm.command.base;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.wodan.zhongjinjicang.common.orm.cache.DynamicCacheKey;
import com.wodan.zhongjinjicang.common.orm.command.SqlCommandExecutor;
import com.wodan.zhongjinjicang.common.orm.metadata.MethodMetaData;
import com.wodan.zhongjinjicang.common.orm.metadata.TableFieldMetaData;
import com.wodan.zhongjinjicang.common.orm.metadata.TableMetaData;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;


/**
 * SqlCommandExecutor子类,用于封装公共代码
 *
 * @ClassName: AbstractSqlCommandExecutor
 * @author Administrator
 * @date 2015-11-5 下午7:42:24
 * @history
 *
 */
public abstract class AbstractSqlCommandExecutor implements SqlCommandExecutor {

	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSqlCommandExecutor.class);

	/**
	 * sql脚本的缓存
	 */
	private final Cache<DynamicCacheKey, String> SQL_SCRIPT_CACHE = CacheBuilder.newBuilder()
			.expireAfterAccess(1, TimeUnit.HOURS).build();

	protected JdbcTemplate jdbcTemplate;

	protected TypeHandlerRegistry typeHandlerRegistry;

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public void setTypeHandlerRegistry(TypeHandlerRegistry typeHandlerRegistry) {
		this.typeHandlerRegistry = typeHandlerRegistry;
	}

	@Override
	public Object execute(TableMetaData tableMetaData, MethodMetaData methodMetaData, Object[] args) {
		Assert.notNull(tableMetaData);

		if (isEntityMustHaveIdField()) {
			TableFieldMetaData idField = tableMetaData.getIdField();
			if (idField == null) {
				throw new IllegalArgumentException("类[" + tableMetaData.getClass().getName() + "]中必须包含id属性");
			}
		}

		try {

			return doExecute(tableMetaData, methodMetaData, args);
		} catch (RuntimeException ex) {
			LOGGER.error("执行方法时抛出异常: ", ex);
/*			if (methodMetaData == null) {
				LOGGER.error("传参如下: tableMetaData=[{}], methodMetaData=[{}], args=[{}]", tableMetaData.getTableClass(),
						this.getClass(), JSON.toJSON(args));
			} else {
				LOGGER.error("传参如下: tableMetaData=[{}], methodMetaData=[{}], args=[{}]", tableMetaData.getTableClass(),
						methodMetaData.getMethod(), JSON.toJSON(args));
			}*/
			throw ex;
		}
	}

	/**
	 * 实体类是否必须包含id字段
	 *
	 * @Description:
	 * @return
	 */
	protected boolean isEntityMustHaveIdField() {
		return false;
	}

	/***
	 * 子类的execute方法
	 *
	 * @Description:
	 * @param tableMetaData
	 * @param methodMetaData
	 * @param args
	 * @return
	 */
	protected abstract Object doExecute(final TableMetaData tableMetaData, MethodMetaData methodMetaData,
										final Object[] args);

	/**
	 * 创建sql语句并缓存
	 *
	 * @Description:
	 * @param keys
	 * @param valueLoader
	 * @return
	 */
	protected String createCachedSqlScript(Callable<String> valueLoader, Object... keys) {
		DynamicCacheKey key = new DynamicCacheKey();
		key.putKeyEntry(this.getClass());
		key.putKeyEntry(keys);
		key.freeze();

		try {
			return SQL_SCRIPT_CACHE.get(key, valueLoader);
		} catch (ExecutionException ex) {
			throw new RuntimeException(ex);
		}
	}

	/**
	 * 插入字段的排序
	 */
	private static Comparator<String> COLUMN_COMPARATOR = new Comparator<String>() {
		@Override
		public int compare(String o1, String o2) {
			return o1.compareTo(o2);
		}
	};

	/**
	 * 将bean转换为列名和value的map
	 *
	 * @Description:
	 * @param entity
	 * @param tableMetaData
	 * @return
	 */
	protected Map<String, Object> resolveBeanToColumnNameMap(Object entity, TableMetaData tableMetaData) {
		try {
			List<TableFieldMetaData> fieldMetaDataList = tableMetaData.getFieldMetaDataList();
			Map<String, Object> fieldValueMap = new TreeMap<>(COLUMN_COMPARATOR);
			for (TableFieldMetaData field : fieldMetaDataList) {
				Method readMethod = field.getReadMethod();
				Object fieldValue = readMethod.invoke(entity);
				if (fieldValue == null) {
					continue;
				}

				fieldValueMap.put(field.getColumnName().toString(), fieldValue);
			}

			return fieldValueMap;
		} catch (IllegalAccessException | InvocationTargetException ex) {
			throw new RuntimeException(ex);
		}
	}

	protected Map<String, Object> convertFieldMapToColumnMap(TableMetaData tableMetaData,
															 Map<String, Object> fieldNameMap) {
		return tableMetaData.convertFieldMapToColumnMap(fieldNameMap, COLUMN_COMPARATOR);
	}

	protected Map<String, Object> resolveConditionColumnNameValueMap(TableMetaData tableMetaData,
																	 MethodMetaData methodMetaData, Object[] args) {
		Map<String, Object> queryConditionFieldNameValueMap = methodMetaData
				.resolveQueryConditionFieldNameValueMap(args);

		return this.convertFieldMapToColumnMap(tableMetaData, queryConditionFieldNameValueMap);
	}

}
